package cn.dengta.context.web;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import cn.dengta.common.context.Env;
import cn.dengta.common.context.RequestContextFilter;
import cn.dengta.common.model.ErrorMessageException;
import cn.dengta.common.model.Messager;
import cn.dengta.common.security.AES;
import cn.dengta.context.model.MessagerStatus;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.StringUtil;
import org.apache.commons.lang3.StringUtils;

/**
 * 增强的、支持 App-Id 解密的 HttpServletRequestWrapper 实现
 */
@Slf4j
public class EnhanceHttpServletRequestWrapper extends HttpServletRequestWrapper {

	/** 用于获取原始的 App-Id 请求头 */
	public static final String ORIGIN_APP_ID = "APP-ID";
	/** 用于获取解密后的 App-Id 请求头 */
	public static final String APP_ID = "app-id";

	protected String appId;

	static AES aes;
	/** 是否兼容明文 */
	static boolean compactPlaintext;

	// ** 第三方回调请求路径的后缀 */
	// public static String[] thirdPartyUriSuffixes = { "Callback", "Notify", "Result" };

	/**
	 * Constructs a request object wrapping the given request.
	 *
	 * @param request The request to wrap
	 * @throws IllegalArgumentException if the request is null
	 */
	public EnhanceHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
	}

	@Override
	public String getHeader(String name) {
		switch (name) {
			case ORIGIN_APP_ID:
				return super.getHeader(APP_ID);
			case APP_ID:
			case "App-Id": { // 暂时不兼容其他畸形的大小写形式
				if (this.appId == null) {
					final String header = super.getHeader(APP_ID);
					if (StringUtil.isEmpty(header)) {
						return this.appId = ""; // 允许为空，兼容 第三方回调请求
						/*
						if (StringUtils.endsWithAny(super.getRequestURI(), thirdPartyUriSuffixes)) {
							return header;
						}
						throw new ErrorMessageException(Messager.status(MessagerStatus.UNTRUSTED));
						*/
					}
					// 没有启用，则不解密；为方便联调测试，开发+测试环境，也支持明文
					if (aes == null || compactPlaintext && StringUtils.countMatches(header, '/') >= 3) {
						return this.appId = header;
					}
					// "秒级时间戳/密文"
					try {
						final String appId = aes.decrypt(header);
						final String[] parts = appId.split("/", 2);
						final long timestampInSecs = Long.parseLong(parts[0]);
						this.appId = parts[1];
						final long nowInSecs = System.currentTimeMillis() / 1000L;
						final long diff = Math.abs(nowInSecs - timestampInSecs);
						if (diff > 10 * 60) { // 前后差异不能超过 10 分钟
							log.warn("客户端时间异常：AppId={}，CID={}，diff={} ms", timestampInSecs + "/" + this.appId, super.getHeader("cid"), (nowInSecs - timestampInSecs));
							throw new ErrorMessageException(Messager.status(MessagerStatus.UNTRUSTED, "请检查系统时间是否设置有误！")).report();
						}
					} catch (Exception e) {
						log.error("解密AppId时出错：" + header, e);
						throw new ErrorMessageException(Messager.status(MessagerStatus.UNTRUSTED), e).report();
					}
				}
				return StringUtil.isEmpty(this.appId) ? null : this.appId;
			}
			default:
				return super.getHeader(name);
		}
	}

	public static void enableAppIdEncrypt(@Nullable String secret, boolean compactPlain) {
		if (secret != null) {
			aes = new AES(secret);
		}
		compactPlaintext = compactPlain;
		RequestContextFilter.setRequestWrapper(EnhanceHttpServletRequestWrapper::new);
	}

	public static void enableAppIdEncrypt(@Nullable String secret) {
		enableAppIdEncrypt(secret, Env.inner());
	}

}
